/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.presentation.rmi ;

import java.rmi.Remote;
import java.rmi.RemoteException;

import javax.rmi.CORBA.Tie;

import java.lang.reflect.Method ;
import java.lang.reflect.InvocationTargetException ;

import org.omg.CORBA.SystemException;
import org.omg.CORBA_2_3.portable.InputStream;
import org.omg.CORBA_2_3.portable.OutputStream;
import org.omg.CORBA.portable.ResponseHandler;
import org.omg.CORBA.portable.UnknownException;
import org.omg.PortableServer.Servant;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAManager;

import com.sun.corba.se.spi.presentation.rmi.PresentationManager ;
import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator ;
import com.sun.corba.se.spi.presentation.rmi.DynamicMethodMarshaller ;

import com.sun.corba.se.spi.orb.ORB ;

import com.sun.corba.se.impl.logging.ORBUtilSystemException ;

import com.sun.corba.se.impl.oa.poa.POAManagerImpl ;

public final class ReflectiveTie extends Servant implements Tie 
{
    private Remote target = null ;
    private PresentationManager pm ;
    private PresentationManager.ClassData classData = null ;
    private ORBUtilSystemException wrapper = null ;

    public ReflectiveTie( PresentationManager pm, ORBUtilSystemException wrapper )
    {
	SecurityManager s = System.getSecurityManager();
 	if (s != null) {
 	    s.checkPermission(new DynamicAccessPermission("access"));
 	}
	this.pm = pm ;
	this.wrapper = wrapper ;
    }

    public String[] _all_interfaces(org.omg.PortableServer.POA poa, 
	byte[] objectId)
    {
	return classData.getTypeIds() ;
    }

    public void setTarget(Remote target) 
    {
        this.target = target;

	if (target == null) {
	    classData = null ;
	} else {
	    Class targetClass = target.getClass() ;
	    classData = pm.getClassData( targetClass ) ;
	}
    }
    
    public Remote getTarget() 
    {
        return target;
    }
    
    public org.omg.CORBA.Object thisObject() 
    {
        return _this_object();
    }
    
    public void deactivate() 
    {
        try{
	    _poa().deactivate_object(_poa().servant_to_id(this));
        } catch (org.omg.PortableServer.POAPackage.WrongPolicy exception){
	    // ignore 
        } catch (org.omg.PortableServer.POAPackage.ObjectNotActive exception){
	    // ignore 
        } catch (org.omg.PortableServer.POAPackage.ServantNotActive exception){
	    // ignore 
        }
    }
    
    public org.omg.CORBA.ORB orb() {
        return _orb();
    }
    
    public void orb(org.omg.CORBA.ORB orb) {
        try {
	    ORB myORB = (ORB)orb ;

            ((org.omg.CORBA_2_3.ORB)orb).set_delegate(this);
        } catch (ClassCastException e) {
	    throw wrapper.badOrbForServant( e ) ;
        }
    }
    
    public org.omg.CORBA.portable.OutputStream  _invoke(String method, 
	org.omg.CORBA.portable.InputStream _in, ResponseHandler reply) 
    {
	Method javaMethod = null ;
	DynamicMethodMarshaller dmm = null;

        try {
            InputStream in = (InputStream) _in;

	    javaMethod = classData.getIDLNameTranslator().getMethod( method ) ;
	    if (javaMethod == null)
		throw wrapper.methodNotFoundInTie( method, 
		    target.getClass().getName() ) ;

	    dmm = pm.getDynamicMethodMarshaller( javaMethod ) ;

	    Object[] args = dmm.readArguments( in ) ;

	    Object result = javaMethod.invoke( target, args ) ;

	    OutputStream os = (OutputStream)reply.createReply() ;

	    dmm.writeResult( os, result ) ; 

	    return os ;
	} catch (IllegalAccessException ex) {
	    throw wrapper.invocationErrorInReflectiveTie( ex, 
		javaMethod.getName(), 
		    javaMethod.getDeclaringClass().getName() ) ;
	} catch (IllegalArgumentException ex) {
	    throw wrapper.invocationErrorInReflectiveTie( ex, 
		javaMethod.getName(), 
		    javaMethod.getDeclaringClass().getName() ) ;
	} catch (InvocationTargetException ex) {
	    // Unwrap the actual exception so that it can be wrapped by an
	    // UnknownException or thrown if it is a system exception.
	    // This is expected in the server dispatcher code.
	    Throwable thr = ex.getCause() ;
	    if (thr instanceof SystemException)
		throw (SystemException)thr ;
	    else if ((thr instanceof Exception) && 
		dmm.isDeclaredException( thr )) {
		OutputStream os = (OutputStream)reply.createExceptionReply() ;
		dmm.writeException( os, (Exception)thr ) ;
		return os ;	
	    } else
		throw new UnknownException( thr ) ;
        }
    }
}
